/**
 * =====================================================================================
 *       @file  ngx_event_accept.c
 *      @brief  
 *
 *  Detailed description starts here.
 *
 *   @internal
 *     Created  2012年05月11日 16时51分24秒 
 *    Revision  1.0.0.0
 *    Compiler  gcc/g++
 *
 *     @author  luis
 * =====================================================================================
 */

#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "ngx_event.h"
#include "ngx_shmtx.h"
#include "ngx_core.h"
#include "ngx_cycle.h"
#include "ngx_connection.h"
#include "ngx_conf_file.h"
#include "ngx_palloc.h"
#include "ngx_os.h"

ngx_os_io_t ngx_io = 
{
    ngx_unix_recv,
    NULL,
    NULL,
    NULL,
    NULL,
    0
};

static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle);

ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
    if (ngx_shmtx_trylock(&ngx_accept_mutex))   
    {
        //获得了锁    
        printf("accept mutex lock");

        if (ngx_accept_mutex_held)
        {
            return NGX_OK;
        }

        ngx_enable_accept_events(cycle);
        
        ngx_accept_events = 0;            
        ngx_accept_mutex_held = 1;
        
        return NGX_OK;
    }

    //locak failed
    if (ngx_accept_mutex_held)
    {
        ngx_disable_accept_events(cycle); 
        ngx_accept_mutex_held = 0;
    }

    return NGX_OK;
}

static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle)
{
    ngx_uint_t          i;
    ngx_listening_t    *ls;
    ngx_connection_t   *c;
    
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++)
    {
        c = ls[i].connection; 
        if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR)
        {
            perror("ngx add event failed");
            return NGX_ERROR;
        }
    }
     
     return NGX_OK;
}

static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle)
{
    ngx_uint_t          i;
    ngx_listening_t    *ls;
    ngx_connection_t   *c;
    
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++)
    {
        c = ls[i].connection; 
        if (!c->read->active)
        {
            continue; 
        }

        if (ngx_del_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR)
        {
            perror("ngx add event failed");
            return NGX_ERROR;
        }
    }
     
     return NGX_OK;
}

static void ngx_close_accepted_connection(ngx_connection_t *c) 
{
    int fd;

    ngx_free_connection(c);

    fd = c->fd;
    c->fd = (int)-1;

    ngx_close_socket(fd);

    if (c->pool)
    {
        ngx_destroy_pool(c->pool); 
    }
}

void ngx_event_accept(ngx_event_t *ev)
{
    socklen_t           socklen; 
    int                 s;
    ngx_event_t        *rev, *wev;
    ngx_listening_t    *ls;
    ngx_connection_t   *c, *lc;
    struct sockaddr_in  sa;

    lc = ev->data;
    ls = lc->listening;
    ev->ready = 0;

    do
    {
        socklen = sizeof(sa); 
        
        s = accept(lc->fd, (struct sockaddr *)&sa, &socklen);
        if (s == -1)
        {
            if (errno == EAGAIN)
            {
                perror("accept not ready");
                return;    
            }

            if (errno == ECONNABORTED)
            {
                if (ev->available)
                {
                    continue;
                }
            }

            return;
        }

        ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;

        c = ngx_get_connection(s);
        if (c == NULL)
        {
            ngx_close_socket(s); 
        }

        ls->pool_size = 512;
        c->pool = ngx_create_pool(ls->pool_size);
        if (c->pool == NULL)
        {
            ngx_close_accepted_connection(c); 
            return;
        }

        c->sockaddr = ngx_palloc(c->pool, socklen);
        if (c->sockaddr == NULL)
        {
            ngx_close_accepted_connection(c); 
            return;
        }

        memcpy(c->sockaddr, &sa, socklen);

        if (ngx_nonblocking(s) == -1)
        {
            printf("set fd:%d nonblock failed:%d", s ,errno);
            ngx_close_accepted_connection(c); 
        }

        c->recv = ngx_recv;         
        //c->send = ngx_send;         
        //c->recv_chain = ngx_recv_chain;         
        //c->send_chain = ngx_send_chain;         

        c->socklen = socklen;
        c->listening = ls;
        c->local_sockaddr = ls->sockaddr;
        c->fd = s;

        rev = c->read;
        wev = c->write;
        wev->ready = 1;

        ls->handler(c);

    } while (ev->available);
}
